#include "server.h"
#include "check.h"
#include "linklist.h"

/***************************************************
函数名:main
功能:聊天软件服务器
传入参数:void
返回值:int
***************************************************/
int main(void)
{
	int re;
	int fd;
	//memset 结构体，为客户端和客户端之间进行传输的主要载体
 	struct message a;
	//客户信息的保存列表;
	datatype e;
	LinkList transfileNode;
	char buf[MAXLEN],str[MAXLEN];
	time_t timep;
	//保存当前的客户端信息;
	//设置客户的默认信息;
 	clientinf clientNode;
 	lientNode.speak = 1;
	my_strcpy(clientNode.name,"***");
	//创建clientlink ,作为客户端的列表的头指针，文件定义在server.h中,供各个方法之间进行公用;
	clientlink=CreateLinkList();//实现
	//进行log的记录
	if((fd=open("chat_log_server.txt",O_RDWR|O_CREAT|O_APPEND,0777)) < 0)
	{
		printf("failure to open chat_log_server!");
		exit(1);
	}
	int lfd; //socket 的文件描述符 
	int cfd; //accept等待的描述符号
	int sfd;
	int rdy;
	struct sockaddr_in sin;
	struct sockaddr_in cin;
	int client[FD_SETSIZE];  /* 客户端连接的套接字描述符数组 */
	int maxi;
	int maxfd;                        /* 最大连接数 */
	fd_set rset;
	fd_set allset;
	socklen_t addr_len;         /* 地址结构长度 */
	char buffer[MAX_LINE];
	int i;
	int n;
	int len;
	int opt = 1;   /* 套接字选项 */
	char addr_p[20];
	/* 对server_addr_in  结构进行赋值  */
	bzero(&sin,sizeof(struct sockaddr_in));   /* 先清零 */
	sin.sin_family=AF_INET;
	sin.sin_addr.s_addr=htonl(INADDR_ANY);  //表示接受任何ip地址   将ip地址转换成网络字节序
	//端口号码写在server.h中
	sin.sin_port=htons(portnumber);         //将端口号转换成网络字节序
	/* 调用socket函数创建一个TCP协议套接口 */
	if((lfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP
	{
		fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
		exit(1);
	}
	/*设置套接字选项 使用默认选项*/
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	/* 调用bind函数 将serer_addr结构绑定到sockfd上  */
	if(bind(lfd,(struct sockaddr *)(&sin),sizeof(struct sockaddr))==-1)
	{
		fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
		exit(1);
	}
	/* 开始监听端口   等待客户的请求 */
	if(my_listen(lfd,20)==-1)
	{
		fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
		exit(1);
	}
	printf("正在连接.......\n");

	maxfd = lfd;      /*对最大文件描述符进行初始化*/
	maxi = -1;
	/*初始化客户端连接描述符集合*/
	//将所有可能存在的客户端初始化为－１.
	for(i = 0;i < FD_SETSIZE;i++)
	{
        client[i] = -1;
	}
	FD_ZERO(&allset);                     /* 清空文件描述符集合 */
	FD_SET(lfd,&allset);                 /* 将监听字设置在集合内 */
	/* 开始服务程序的死循环 */
	while(1)
	{
		rset = allset;
		/*得到当前可以读的文件描述符数*/
		rdy = select(maxfd + 1, &rset, NULL, NULL, NULL);
		//是监听socket进行返回
		if(FD_ISSET(lfd, &rset))
		{
			addr_len = sizeof(sin);

			/* 接受客户端的请求 */
			if((cfd=accept(lfd,(struct sockaddr *)(&cin),&addr_len))==-1)
			{
				fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
				exit(1);
			}
           		/*查找一个空闲位置*/
			for(i = 0; i<FD_SETSIZE; i++)
			{
				if(client[i] <= 0)
				{
					client[i] = cfd;   /* 将处理该客户端的连接套接字设置到该位置 */
					break;
				}
			}
		    /* 太多的客户端连接   服务器拒绝俄请求  跳出循环 */
			if(i == FD_SETSIZE)
			{
				printf("too many clients");
				exit(1);
			}
			FD_SET(cfd, &allset);     /* 设置连接集合 */
			if(cfd > maxfd)                  /* 新的连接描述符 */
			{
				maxfd = cfd;
			}
			if(i > maxi)
			{
				maxi = i;
			}
			if(--rdy <= 0)                /* 减少一个连接描述符 */
			{
				continue;
			}
		}
		/* 对每一个连接描述符做处理 */
		for(i = 0;i< FD_SETSIZE;i++)
		{
			if((sfd = client[i]) < 0)
			{
				continue;
			}

			if(FD_ISSET(sfd, &rset))
			{
				/*如果没有可以读的套接字   退出循环*/
				if(--rdy < 0)
				{
					break;
				}
				re = recv(sfd,&a,sizeof(a)+1,0);
				//将接受到的描述符号的信心加入到当前的客户端信息当中;
				clientNode.decr = sfd;
				my_strcpy(clientNode.name,a.name);
				if(re == 0)                                /*客户端断开连接*/
				{
		            		server_exit(&a, &clientNode) ;
					fflush(stdout);                                    /* 刷新 输出终端 */
					my_close(sfd);
					FD_CLR(sfd, &allset);                        /*清空连接描述符数组*/
					client[i] = -1;
				}
				else
				{
				    if(my_strcmp(a.flag,"login") == 0)      /*客户端请求登录*/
                    			{
                        			if(overlap(a) == 0)               /*若该用户名不在线*/
             					{
        			        		server_login(&a,&clientNode);    /*处理登录请求*/
                        			}else                                 /*若该用户名已在线*/
					    	{
				            		strcpy(a.msg,"overlap");
					  	    	my_send(clientNode.decr,&a,sizeof(struct message),0);/*返回重名提示*/
						}
					        continue;
                    			}
                   		 else if(my_strcmp(a.flag,"reg") == 0)   /*客户端请求注册*/
                    		 {
		        		server_reg(&a,&clientNode);       /*处理注册请求*/
                        		continue;
                      		 }
 	                         else if (my_strcmp(a.flag,"all") == 0)    /*客户端请求广播*/
                   		 {
                        		if(server_all(&a,&clientNode) == 1)   /*处理广播请求*/
                        		{
						memset(str,0,strlen(str));
						time (&timep);
						sprintf(str,"%s%s TO %s: %s\n",ctime(&timep),a.name,a.flag,a.msg);
						printf("%s",str);
						my_write(fd,str,strlen(str));      /*将广播写入聊天记录*/
                        		}
					continue;
                    		}
 	                        else if(my_strcmp(a.flag,"personal") == 0)  /*客户端请求私聊*/
				{
					if(server_personal(&a,&clientNode) == 1) /*处理私聊请求*/
					{
						memset(str,0,strlen(str));
						time (&timep);
						sprintf(str,"%s%s TO %s: %s\n",ctime(&timep),a.name,a.addressee,a.msg);
		     			        printf("%s",str);
						my_write(fd,str,strlen(str));       /*将私聊写入聊天记录*/
						}
					}
				else if(my_strcmp(a.flag,"view") == 0)    /*客户端请求查看在线用户*/
                    		{
                        		server_view(&a,&clientNode);        /*处理查看在线用户请求*/
                        		continue;
                    		}
		            	else if(my_strcmp(a.flag,"admin_kick") == 0) /*管理员替人请求*/
		            	{
		                	int closefd;
		                	closefd = server_admin_kick(&a,&clientNode);  /*处理管理员替人请求*/
					FD_CLR(closefd, &allset);                      /*清空连接描述符数组*/
					int j;
					for(j = 0;j< FD_SETSIZE;j++)
					{
						if(closefd == client[j])
						{
						        client[j]=-1;
							break;
						}
					}
					continue;
		                }
		            	else if(my_strcmp(a.flag,"admin_screen") == 0)/*管理员禁言请求*/
		            	{
		                	server_admin_screen(&a,&clientNode);/*处理管理员禁言请求*/
					continue;
		            	}
 		            	else if(my_strcmp(a.flag,"trans") == 0) /*客户端文件传输请求*/
                    		{

				        server_trans(&a,&clientNode);    /*处理客户端文件传输请求*/
                        		continue;
                    		}
		            	else if(my_strcmp(a.flag,"transf") == 0) /*客户端文件数据传输请求*/
		            	{
				       server_transf(&a);        /*传送文件*/
			               continue;
		            	}
                }
			}
		}
    }
	close(lfd);
	return 0;
}

